package com.wxy.controller;

import com.wxy.response.ApiResponse;
import com.wxy.response.FileInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {

    @Value("${file.repository}")
    private String repository;

    private final List<String> suffixList = Arrays.asList(".txt", ".properties", ".xml");

    /**
     * 获取文件列表
     *
     * @param name   文件名称
     * @param folder 当前文件夹
     * @return
     */
    @GetMapping("/list")
    public ApiResponse list(@RequestParam(required = false) String name, @RequestParam(required = false) String folder) {
        File file = new File(getRealPath(folder));
        if (file.exists() && file.isDirectory()) {
            List<FileInfo> list = readList(file);
            // 搜索(忽略大小写)
            if (StringUtils.isNotBlank(name)) {
                list = list.stream().filter(f -> f.getName().toLowerCase().contains(name)).collect(Collectors.toList());
            }
            // 排序
            list.sort(Comparator.comparing(FileInfo::getName));

            HashMap<String, Object> data = new HashMap<>();
            data.put("parent", hideRealPath(file.getPath()));
            data.put("list", list);
            return ApiResponse.success(data);
        }
        return ApiResponse.error();
    }

    /**
     * 获取文件夹真实路径
     *
     * @param filePath
     * @return
     */
    private String getRealPath(String filePath) {
        return new File(repository).getAbsolutePath() + (StringUtils.isBlank(filePath) ? "" : filePath);
    }

    /**
     * 隐藏文件真实路径
     *
     * @param filePath
     * @return
     */
    private String hideRealPath(String filePath) {
        String path = new File(repository).getAbsolutePath();
        return filePath.substring(filePath.indexOf(path) + path.length());
    }

    /**
     * 读取文件列表
     *
     * @param file
     * @return
     */
    private List<FileInfo> readList(File file) {
        File[] files = file.listFiles();
        List<FileInfo> list = new ArrayList<>();
        for (File f : files) {
            list.add(new FileInfo(f.getName(), hideRealPath(f.getPath()), f.isDirectory(), f.length(), f.lastModified()));
        }
        return list;
    }

    @PostMapping("/rename")
    public ApiResponse rename(@RequestParam String filePath, @RequestParam String newName) {
        File file = new File(getRealPath(filePath));
        File file1 = new File(file.getParent() + File.separator + newName);
        if (file.exists() && !file1.exists()) {
            if (file.renameTo(file1)) {
                log.info("重命名文件：oldName[{}]，newName[{}]", file.getAbsolutePath(), file1.getAbsolutePath());
                return ApiResponse.success();
            }
        }
        return ApiResponse.error();
    }

    @GetMapping("/download")
    public void download(HttpServletResponse response, @RequestParam String filePath) throws IOException {
        File file = new File(getRealPath(filePath));
        if (file.exists()) {
            if (file.isDirectory()) {
                // 打包下载
            } else {
                writeToResponse(response, file.getName(), new FileInputStream(file));
                log.info("下载文件：file[{}]", file.getAbsolutePath());
            }
        }
    }

    private static void zipFiles(File[] srcfile, File zipfile) {
        byte[] buf = new byte[1024];
        try {
            //ZipOutputStream类：完成文件或文件夹的压缩
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipfile));
            for (File file : srcfile) {
                FileInputStream in = new FileInputStream(file);
                out.putNextEntry(new ZipEntry(file.getName()));
                int len;
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
                out.closeEntry();
                in.close();
            }
            out.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void writeToResponse(HttpServletResponse response, String fileName, InputStream in) throws IOException {
        response.setContentType("application/force-download");// 设置强制下载不打开
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));// 设置文件名
        BufferedInputStream bin = new BufferedInputStream(in);
        OutputStream out = response.getOutputStream();
        byte[] buffer = new byte[1024 * 1024 * 10];
        int i = bin.read(buffer);
        while (i != -1) {
            out.write(buffer, 0, i);
            i = bin.read(buffer);
        }
    }

    @PostMapping("/upload")
    public ApiResponse upload(@RequestParam("file") MultipartFile[] files, @RequestParam(required = false) String folder) throws IOException {
        File file1 = new File(getRealPath(folder));
        if (files.length > 0 && file1.exists()) {
            for (MultipartFile f : files) {
                String filename = f.getOriginalFilename().replaceAll(" ", "_");
                File dest = new File(file1.getPath() + File.separator + filename);
                writeFile(dest, f.getInputStream());
                log.info("上传文件：file[{}]", dest.getAbsolutePath());
            }
            return ApiResponse.success();
        }
        return ApiResponse.error();
    }

    @GetMapping("/back")
    public ApiResponse goBack(@RequestParam String folder) {
        File file = new File(getRealPath(folder));
        if (file.exists()) {
            File root = new File(repository);
            return ApiResponse.success(file.getAbsolutePath().equals(root.getAbsolutePath())
                    ? hideRealPath(file.getPath()) : hideRealPath(file.getParent()));
        }
        return ApiResponse.error();
    }

    @PostMapping("/delete")
    public ApiResponse delete(@RequestParam String filePath) {
        File file = new File(getRealPath(filePath));
        if (file.exists() && deleteFile(file)) {
            log.info("删除文件：file[{}]", file.getAbsolutePath());
            return ApiResponse.success();
        }
        return ApiResponse.error();
    }

    @PostMapping("/deleteMore")
    public ApiResponse deleteMore(@RequestParam String filePaths) {
        String[] split = filePaths.split(";");
        for (String s : split) {
            File file = new File(getRealPath(s));
            if (file.exists() && deleteFile(file)) {
                log.info("删除文件：file[{}]", file.getAbsolutePath());
            }
        }
        return ApiResponse.success();
    }

    @PostMapping("/folder")
    public ApiResponse newFolder(@RequestParam String filePath) {
        File file = new File(getRealPath(filePath));
        if (!file.exists()) {
            file.mkdir();
            log.info("新建文件夹：file[{}]", file.getAbsolutePath());
            return ApiResponse.success();
        }
        return ApiResponse.error();
    }

    private void writeFile(File file, InputStream in) throws IOException {
        BufferedInputStream bin = new BufferedInputStream(in);
        FileOutputStream out = new FileOutputStream(file);
        byte[] buffer = new byte[1024 * 1024 * 10];
        int i = bin.read(buffer);
        while (i != -1) {
            out.write(buffer, 0, i);
            i = bin.read(buffer);
        }
    }

    private boolean deleteFile(File file) {
        if (!file.exists()) {
            return false;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                deleteFile(f);
            }
        }
        return file.delete();
    }

    /**
     * 移动文件
     *
     * @param filePaths
     * @param folder
     * @return
     */
    @PostMapping("/move")
    public ApiResponse move(@RequestParam String filePaths, @RequestParam String folder) {
        File file = new File(getRealPath(folder));
        if (file.exists()) {
            String[] split = filePaths.split(";");
            for (String s : split) {
                File file1 = new File(getRealPath(s));
                if (file1.exists()) {
                    File file2 = new File(file.getPath() + File.separator + file1.getName());
                    file1.renameTo(file2);
                    log.info("移动文件夹：form[{}]，to[{}]", file1.getAbsolutePath(), file2.getAbsolutePath());
                }
            }
            return ApiResponse.success();
        }
        return ApiResponse.error();
    }

    @GetMapping("/view")
    public ApiResponse view(@RequestParam String filePath) {
        File file = new File(getRealPath(filePath));

        if (file.exists() && suffixList.contains(filePath.substring(filePath.lastIndexOf(".")))) {
            StringBuilder sb = new StringBuilder();
            try {
                BufferedReader reader = new BufferedReader(new FileReader(file));
                String tempStr;
                while ((tempStr = reader.readLine()) != null) {
                    sb.append(tempStr).append("\n");
                }
            } catch (IOException e) {
                sb.append("文件获取失败");
            }
            return ApiResponse.success(sb.toString());
        }
        return ApiResponse.error();
    }

    @GetMapping("/tree")
    public ApiResponse tree() {
        List<Map<String, Object>> list = new ArrayList<>();
        // 根目录
        File root = new File(repository);
        Map<String, Object> map = new HashMap<>();
        map.put("title", "全部文件");
        map.put("id", hideRealPath(root.getAbsolutePath()));
        map.put("children", getFolderTreeData(root));
        list.add(map);
        return ApiResponse.success(list);
    }

    /**
     * 获取文件夹树
     *
     * @param file
     * @return
     */
    private List<Map<String, Object>> getFolderTreeData(File file) {
        List<Map<String, Object>> list = new ArrayList<>();
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isDirectory()) {
                Map<String, Object> map = new HashMap<>();
                map.put("title", f.getName());
                map.put("id", hideRealPath(f.getAbsolutePath()));
                map.put("children", getFolderTreeData(f));
                list.add(map);
            }
        }
        return list;
    }
}
